package com.study.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSON;
import com.study.anno.Idempotence;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zhuzi
 * 2024/6/27 下午3:34
 */
@Aspect
@Component
public class IdemoptenceAspect {

    @Resource
    RedisTemplate redisTemplate;

    /**
     * 幂等性接口环绕通知
     * 核心需要找到一些唯一的标记
     * 1 拿到请求IP
     * 2 拿到请求的方法名称
     * 3 拿到参数值
     * 4 达到当前用户ID
     * 5 组合成唯一的标记
     * 6 redis setnx方法
     * 7 拦截还是执行目标方法
     */

    //@Around(value = "@annotation(com.study.anno.Idempotence)")
    //还能这么写
    @Around(value = "@annotation(idempotence)")
    public Object idemoptence(ProceedingJoinPoint joinPoint, Idempotence idempotence) {
        Object result = null;
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String clientIP = ServletUtil.getClientIP(request, null);

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        Method method = signature.getMethod();
        //方法名和类名
        String name = method.getName();
        Class<?> declaringClass = method.getDeclaringClass();
        String typeName = declaringClass.getTypeName();

        //请求参数
        Object[] args = joinPoint.getArgs();
        List<Object> collect = Arrays.stream(args)
                //ServletRequest，ServletResponse，MultipartFile不能被序列化，需要排除之后再做序列化。https://www.cnblogs.com/stxyg/p/14964788.html
                .filter(ar -> !(ar instanceof ServletRequest) && !(ar instanceof ServletResponse) && !(ar instanceof MultipartFile))
                .collect(Collectors.toList());
        String argsStr = JSON.toJSONString(collect);

        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            RequestBody annotation = parameter.getAnnotation(RequestBody.class);
            Annotation[] annotations = parameter.getAnnotations();

            for (Annotation ann : annotations) {
                System.out.println("注解" + ann);
            }
            System.out.println("Parameter name: " + parameter.getName());
            if (annotation != null) {
                System.out.println("Annotation value: " + annotation.toString());
            }
        }
        //构建唯一标识
        String uniqueKey = StrUtil.format("{}-{}-{}-{}", clientIP, typeName, name, argsStr);

        System.out.println(uniqueKey);
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("idemoptence:" + uniqueKey, "", Duration.ofSeconds(1));
        if (flag) {
            try {
                result = joinPoint.proceed(args);
                return result;
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
        return "重复请求";
    }


}
